[国家集训队]聪聪可可

2020-03-16
国家集训队

题意

求树上路径长度为3的倍数的数量,$n\leq 2\cdot 10^4$

题解

点分治,开余数的桶合并,别忘了长度为0

调试记录

没有约分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <cstdio>
#include <algorithm>
#define LL long long
const int maxn = 2e4 + 5;
using namespace std;
struct E{
int to, nxt, l;
}e[maxn << 1];
int head[maxn], tot = 0;
void addedge(int u, int v, int l){
e[++tot].to = v, e[tot].nxt = head[u], e[tot].l = l;
head[u] = tot;
}
bool vis[maxn];
int n, rt, sz[maxn], Max[maxn];
void getRt(int cur, int fa, int totsz){
sz[cur] = 1; Max[cur] = -1;
for (int i = head[cur]; i; i = e[i].nxt){
int v = e[i].to;
if (v != fa && !vis[v]){
getRt(v, cur, totsz);
sz[cur] += sz[v];
Max[cur] = max(Max[cur], sz[v]);
}
} Max[cur] = max(Max[cur], totsz - sz[cur]);
if (Max[cur] < Max[rt]) rt = cur;
}
int cnt[3], dis[maxn];
void getDis(int cur, int fa){
cnt[dis[cur] % 3]++;
for (int i = head[cur]; i; i = e[i].nxt){
int v = e[i].to;
if (v != fa && !vis[v]){
dis[v] = (dis[cur] + e[i].l) % 3;
getDis(v, cur);
}
}
}
int calc(int cur, int d){
dis[cur] = d % 3; cnt[0] = 0, cnt[1] = 0, cnt[2] = 0;
getDis(cur, 0);
return cnt[0] * (cnt[0] - 1) / 2 + cnt[1] * cnt[2];
}
LL ans = 0;
void dfs(int cur, int totsz){
vis[cur] = 1;
ans += calc(cur, 0);
for (int i = head[cur]; i; i = e[i].nxt){
int v = e[i].to;
if (!vis[v]){
ans -= calc(v, e[i].l);
int ssz = sz[v] > sz[cur] ? totsz - sz[cur] : sz[v];
rt = 0; getRt(v, cur, ssz);
dfs(rt, ssz);
}
}
}
LL _gcd(LL x, LL y){
return (!y) ? x : _gcd(y, x % y);
}
int main(){
scanf("%d", &n);
for (int u, v, l, i = 1; i < n; i++){
scanf("%d%d%d", &u, &v, &l); l %= 3;
addedge(u, v, l); addedge(v, u, l);
} Max[0] = 0x3f3f3f3f; rt = 0;
getRt(1, 0, n); dfs(rt, n);
LL a = ans * 2 + n, b = 1ll * n * n;
LL g = _gcd(a, b);
printf("%lld/%lld\n", a / g, b / g);
return 0;
}